package com.zhaorujie.rpc.core.spi;

import cn.hutool.core.io.resource.ResourceUtil;
import com.zhaorujie.rpc.core.exception.RpcException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 赵汝杰
 * @version v1.0
 * @description spi加载器：加载META_INF下的文件
 * @date 2024/3/13
 **/
public class SpiLoader {

    private static final Logger log = LoggerFactory.getLogger(SpiLoader.class);

    /**
     * 存储已加载的类：接口名 => (key => 实现类)
     */
    private static final Map<String, Map<String, Class<?>>> LOADER_MAP = new HashMap<>();

    /**
     * 对象实例缓存（避免重复new）：路径 => 对象实例，单例
     */
    private static final Map<String, Object> INSTANCE_CACHE = new ConcurrentHashMap<>();

    /**
     * rpc的spi目录
     */
    private static final String RPC_SPI_DIC = "META-INF/rpc/";

    /**
     * 加载某个类型
     * @param loadClass
     */
    public static void load(Class<?> loadClass) {
        log.info("加载类型为 {} 的 SPI", loadClass.getName());
        // 扫描路径，用户自定义的 SPI 优先级高于系统 SPI
        Map<String, Class<?>> keyClassMap = new HashMap<>();
        List<URL> resources = ResourceUtil.getResources(RPC_SPI_DIC + loadClass.getName());
        // 读取每个资源文件
        for (URL resource : resources) {
            try (InputStreamReader inputStreamReader = new InputStreamReader(resource.openStream());
                 BufferedReader bufferedReader = new BufferedReader(inputStreamReader)){
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    String[] array = line.split("=");
                    if (array.length > 1) {
                        keyClassMap.put(array[0], Class.forName(array[1]));
                    }
                }
            } catch (Exception e) {
                log.error("spi resource load error", e);
            }
        }
        LOADER_MAP.put(loadClass.getName(), keyClassMap);
    }

    /**
     * 获取某个接口的实例
     * @param clazz
     * @param key
     * @return
     * @param <T>
     */
    public static <T> T getInstance(Class<T> clazz, String key) {
        String name = clazz.getName();
        Map<String, Class<?>> keyClassMap = LOADER_MAP.get(name);
        if (keyClassMap == null) {
            throw new RpcException(String.format("SpiLoader 未加载 %s 类型", name));
        }

        if (!keyClassMap.containsKey(key)) {
            throw new RpcException(String.format("SpiLoader 的 %s 不存在 key=%s 的类型", name, key));
        }

        // 获取到要加载的实现类型
        Class<?> loadClass = keyClassMap.get(key);
        String implClassName = loadClass.getName();
        if (!INSTANCE_CACHE.containsKey(implClassName)) {
            try {
                INSTANCE_CACHE.put(implClassName, loadClass.getConstructor().newInstance());
            } catch (Exception e) {
                String errorMsg = String.format("%s 类实例化失败", implClassName);
                throw new RpcException(errorMsg, e);
            }
        }
        return (T) INSTANCE_CACHE.get(implClassName);
    }
}
